package dm_pm;


import java.util.Random;
import java.util.Vector;

/**
 * 
 * This class will implement the algorithmDM
 * described in the pseudocode.
 * 
 * @author Tom Deering
 * @author Dossay Oryspayev
 *
 */

public class algorithmDM {
	private Vector<Task> V_t;
	private Vector<Processor> V_p;
	private long maxSimLength;
	private int deltaP;
	
	/**
	 * Constructor for this class.
	 * 
	 * @param Set of tasks.
	 * @param Set of processors.
	 * 
	 */
	public algorithmDM(Vector<Task> V_t, Vector<Processor> V_p, long maxSimLength, int deltaP) {
		this.V_t = V_t;
		this.V_p = V_p;
		this.maxSimLength = maxSimLength;
		this.deltaP = deltaP;
		
		if(this.V_t.size()==0 ||
		   this.V_p.size()==0 ||
		   this.maxSimLength<=0 ||
		   this.deltaP<0) {
			throw new IllegalArgumentException(
			"Please check that Vectors are not empty,\n"+
			"max. simulation length is positive, and\n" +
			"delta for period is nonnegative.");			
		}
	}
	
	/**
	 * This method starts the DM algorithm.
	 *  
	 * @return Result as FAILURE or SUCCESS.
	 */
	public RESULT startAlgorithm() {
		Vector<Instance> V_i = new Vector<Instance>();
		

		for(long lastTick=0, currentTick=0; currentTick<this.maxSimLength; currentTick++) {
			V_i.clear();
			long nearestNextInstance = updateInstances(currentTick, V_i);
			DM_PM.sortInstances(V_i);
			
			// We do not decrement at time 0.
			if(currentTick!=0) {
				decrementComputationTimes(currentTick, lastTick);
			}
			
			if(V_i.size()>0) {
				if(mapInstances2Processors(V_i, this.V_p, currentTick)==RESULT.FAILURE) {
					return RESULT.FAILURE;
				}
			}
			
			//Skip ahead to the future, remembering the loop increment
			lastTick = currentTick;
			currentTick += nearestNextInstance-currentTick-1;
		}
		return RESULT.SUCCESS;
	}
		
	private RESULT mapInstances2Processors(
			Vector<Instance> V_i, Vector<Processor> V_np, long currentTick) {
		boolean indicator = false;
		int i =0;
		//for(int i=0; i<V_i.size(); i++) 
		for(;;) {
			if(V_i.size()>0){
			indicator = false;
			Instance instance = V_i.elementAt(i);
			
			for(int np=0; np<V_np.size(); np++) {
				Processor processor = V_np.elementAt(np);
				
				if(DM_PM.R_ik(instance, processor)<=instance.getDeadline()) {
					indicator = true;
					V_np.elementAt(np).instanceQueue.add(instance);
					V_i.removeElementAt(i);
					// do we need this?
					DM_PM.sortInstances(processor.instanceQueue);
					break;
				}
			}
			
			// Meaning that processor to fit this
			// instance fully was not found, so
			// we have to try to split this instance.
			if(indicator==false) {
				if(DM_PM.doDM_PM(V_i, i, V_np, currentTick)==RESULT.FAILURE) {
					return RESULT.FAILURE;
				}
				else {
					V_i.removeElementAt(i);
				}
			}
			}
			else {
				break;
			}
		}
		return RESULT.SUCCESS;
	}	
	
	
	/**
	 * Decrement computation time of first instance on every processor,
	 * if necessary remove it if its computation time is not positive.
	 * 
	 * 
	 */
	private void decrementComputationTimes(long currentTick, long lastTick) {
		long toDecThisProc;		
		long maxDecPerInst;
		
		for(int e=0; e<V_p.size(); e++) {
			Processor p = V_p.elementAt(e);
			DM_PM.sortInstances(V_p.elementAt(e).instanceQueue);
			
			//For each processor, decrement the amount of time that has passed
			toDecThisProc = currentTick-lastTick;		
			for(int j=0; j<V_p.elementAt(e).instanceQueue.size(); j++) {
				Instance i = p.instanceQueue.elementAt(j);
				long st = i.getStartTime();
				if(st<currentTick) {
					//Decrement computation time by Min{currentTick-st,toDecrement,i.getComputation()}
					maxDecPerInst = currentTick-st>toDecThisProc?toDecThisProc:currentTick-st;
					maxDecPerInst = maxDecPerInst>i.getComputation()?i.getComputation():maxDecPerInst;
					i.setComputation(i.getComputation()-maxDecPerInst);
					
					//Remove instance if finished
					if(i.getComputation()<=0) 
						p.instanceQueue.removeElementAt(j);
					
					//Move to next proc if all time has been decremented
					if((toDecThisProc -= maxDecPerInst)==0){
						break;
					}
				}
			}
			
			
			/*
			if(processor.instanceQueue.isEmpty()==false) {	
				Instance instance = processor.instanceQueue.elementAt(0);
				if(instance.isShared()) {
					if(instance.getStartTime()<tick) {
						instance.setComputation(instance.getComputation()-1);
						if(instance.getComputation()<=0) {
							processor.instanceQueue.removeElementAt(0);
						}
					}
				}
				else if(!instance.isShared()){
					instance.setComputation(instance.getComputation()-1);
					if(instance.getComputation()<=0) {
						processor.instanceQueue.removeElementAt(0);
					}
				}
			}
			
			*/
			
			
			
		}
	}
	
	/**
	 * Every tick we update the set of instances with those tasks
	 * that would be generated.
	 * 
	 * @param currentTick
	 * @return The soonest time any task will generate a new instance
	 */
	private long updateInstances(long currentTick, Vector<Instance> V_i) {
		long nextInstanceOfAnyTask = Long.MAX_VALUE;
		for(Task task : this.V_t) {
			if(task.getNextInstance()<=currentTick) {
				Instance instance = 
						new Instance(task.getComputationTime(),
								     task.getDeadline(),
								     task.getId(),
								     currentTick);
				instance.setShared(false);
				Random generator = new Random();
				int value = (int)(this.deltaP*task.getPeriod());
				if(value==0) {
					task.setNextInstance(currentTick+task.getPeriod());
				}
				else {
					task.setNextInstance(currentTick+task.getPeriod()+generator.nextInt(value));
				}
				instance.setPeriod(task.getNextInstance());
				instance.setPriority(instance.getDeadline());
				V_i.add(instance);
			}
			if(task.getNextInstance()<nextInstanceOfAnyTask){
				nextInstanceOfAnyTask = task.getNextInstance();
			}
		}
		return nextInstanceOfAnyTask;
	}	
		
}
